// Copyright (c) 2009 Konstantin Yegupov
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

_buffer = [];

Element = function(x, y, text, col, back) {
    this.x = x;
    this.y = y;
    this.text = text;
    this.col = col;
    this.back = back;
}
Element.prototype.absx = function() {
    return this.x+this.block.x;
}
Element.prototype.absy = function() {
    return this.y+this.block.y;
}


Block = function(x, y, w, h, back) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.back = back;
    this.els = [];
}

Block.prototype.add = function(el) {
    el.block = this;
    this.els.push(el);
}

function newspan(text) {
    var span = document.createElement('span');
    span.appendChild(document.createTextNode(text));
    return span;
}

function pushchunk(bufrow, el, x0, x1) {
    var s = el.text.substr(x0-el.absx(), x1-x0);
    var col = el.col ? 'color:'+el.col+';' : '';
    if (s.length>0) {
        var sp = newspan(s);
        sp.style.color = el.col;
        sp.style.backgroundColor = el.back;
        sp.tmel = el;
        if (el.onclick!=undefined) {
            sp.onclick = el.onclick;
            sp.style.cursor = 'pointer';
        } else {
            sp.onclick = TextMode.defaultClick;
        }
        bufrow.push(sp);
    }
    return s.length;
}

function pushspaces(bufrow, n) {
    var sp = newspan(spaces(n));
    sp.onclick = TextMode.defaultClick;
    bufrow.push(sp);
    return n;
}

function strdup(c, n) {
    var s = [];
    for (var i=0; i<n; i++) {s.push(c)}
    return s.join('');
}

function spaces(n) {
    return strdup(' ', n);
}

Renderer = {base_blocks:[], block_providers:[], w:80, h:25};

Renderer.go = function() {
    var blocks = this.base_blocks;
    for (var i=0; i<this.block_providers.length; i++) {
        blocks = blocks.concat(this.block_providers[i].provide_blocks());
    }
    var w = Renderer.w;
    var h = Renderer.h;
    var cont_id = Renderer.target;
    
    var row_els = {};
    for (i=0; i<blocks.length; i++) {
        var block = blocks[i];
        var blockspaces = spaces(block.w);
        for (var y=0; y<block.h; y++) {
            var el = new Element(0, y, blockspaces, null, block.back);
            el.block = block;
            if (row_els[block.y+y]==undefined) row_els[block.y+y] = [];
            row_els[block.y+y].push(el);
        }
        for (var j=0; j<block.els.length; j++) {
            var el = block.els[j];
            row_els[el.absy()].push(el);
        }
    }
    _buffer = [];
    var bufs = [];
    for (var y=0; y<h; y++) {
        var rowels = row_els[y];
        var bufrow = [];
        _buffer.push(bufrow);
        if (rowels==undefined) {
            bufs.push('');
            continue;
        }
        
        parts = [];
        
        for (var i=0; i<rowels.length; i++) {
            var el = rowels[i];
            var x0 = el.absx();
            var x1 = x0 + el.text.length;
            if (x1>w) x1 = w;
            if (x1>el.block.x+el.block.w) x1=el.block.x+el.block.w;
            var j=0;
            while (j<parts.length && parts[j][1] <= x0) j++; // skip parts before this element
            var cut0 = j;
            var newp = [[x0, x1, el]];
            while (j<parts.length && parts[j][0] < x1) {
                var p = parts[j];
                if (x0>p[0]) {
                    // Save start
                    newp.unshift([p[0],x0,p[2]]);
                }
                
                if (x1<p[1]) {
                    // Save end
                    newp.push([x1,p[1],p[2]]);
                }
                j++;
            }
            parts = parts.slice(0, cut0).concat(newp, parts.slice(j));
        }
        var xpos=0;
        for (var j=0; j<parts.length; j++) {
            var part = parts[j];
            if (xpos<part[0]) {
                xpos += pushspaces(bufrow, part[0]-xpos);
            }
            xpos += pushchunk(bufrow, part[2], part[0], part[1]);
        }
        bufs.push(bufrow);
    }
    var pre = document.createElement('pre');
    for (var i=0; i<bufs.length; i++) {
        var bufrow = bufs[i];
        for (var j=0; j<bufrow.length; j++) {
            pre.appendChild(bufrow[j]);
        }
        pre.appendChild(document.createTextNode('\n'));
    }
    document.getElementById(cont_id).innerHTML = '';
    document.getElementById(cont_id).appendChild(pre);
}

TextMode={};
TextMode.defaultClick = function(){};